#!/usr/bin/python
# -*- coding: UTF-8 -*-

from utils.log.log import Log
from utils.utils import (
    ShellOutput,
    get_host_ip,
    get_host_name,
    get_file_data,
)
from config.config import PROCESS_MEM_MAX_LIMIT
from utils.smtp.smtp import Smtp
import psutil as p

log = Log("process monitor ")


class ProcessMonitor(object):
    """ProcessMonitor 进程监控"""

    def __init__(self, *, desc=None, name=None, number=None, pid_file=None):
        super(ProcessMonitor, self).__init__()
        self.desc = desc
        self.name = name
        self.number = number
        self.pid_file = pid_file
        self.ids = []
        self.pid = None
        self.smtp = Smtp()
        self.mem_max_limit = PROCESS_MEM_MAX_LIMIT * 1000

    def _getProcessIdList(self):
        str = ShellOutput(
            "ps aux|grep %s |grep -v grep|awk '{print $2}'|xargs" % self.name
        )
        sa = str.split()
        dst = []
        for t in sa:
            dst.append(int(t))

        # dst.sort()
        return dst

    def _getMem(self, pid):
        mem = ShellOutput("cat /proc/%u/status |grep VmRSS|awk '{print $2}'" % pid)

        return int(mem)

    def _memToStr(self, mem):
        if mem > 1000:
            return str(mem / 1000) + " m"

        return str(mem) + " k"

    def Init(self):
        # 优先判断是否是pid_file检查
        if self.pid_file != None:
            self.pid = int(get_file_data(self.pid_file))
        else:
            self.ids = self._getProcessIdList()
        log.Debug(
            "enter process init desc=",
            self.desc,
            "name=",
            self.name,
            "number=",
            self.number,
            "ids=",
            self.ids,
        )

    def _pid_file_check(self):
        if self.pid_file == None:
            return
        npid = int(get_file_data(self.pid_file))
        if npid < 0:
            log.Error("pid_file[%s] can not get pid" % self.pid_file)
            return
        log.Debug("goto check npid=", npid)
        if self.pid == None:
            self.pid = npid
        # 看看是不是进程重启过
        if self.pid != npid:
            self.smtp.Send(
                "server %s[%s] process warning %s maybe restart aready from %d to %d!!!!!!!!!!!!!!!!!!!"
                % (get_host_ip(), get_host_name(), self.name, self.pid, npid)
            )
            self.pid = npid
            return
        if p.pid_exists(self.pid) == False:
            self.smtp.Send(
                "server %s[%s] process err %s[%d] is not exist now !!!!!!!!!!!!!!!!!!!"
                % (get_host_ip(), get_host_name(), self.name, self.pid)
            )
        else:
            mem = self._getMem(self.pid)

            log.Debug(
                "%s pid=%d mem=%s is alive!!!!!" % (self.name, self.pid, _memToStr(mem))
            )

            if mem >= self.mem_max_limit:
                self.smtp.Send(
                    "server %s[%s] process warning %s[%d] mem is too big[%d m] now !!!!!!!!!!!!!!!!!!!"
                    % (get_host_ip(), get_host_name(), self.name, self.pid, mem / 1000)
                )
        return

    def _pids_check(self):
        if self.pid_file != None:
            return
        # 先判断进程个数对不对
        nids = self._getProcessIdList()
        log.Debug("process check get new %s ids=" % self.name, nids)

        # 可能是刚检查到，直接返回
        if len(self.ids) <= 0:
            if len(nids) <= 0:
                return
            self.smtp.Send(
                "server %s[%s] process restart now %s process number changed from %d to %d"
                % (get_host_ip(), get_host_name(), self.name, len(self.ids), len(nids))
            )
            self.ids = nids
            return

        # 比较个数有没有变化
        if len(self.ids) != len(nids):
            log.Debug("%s process ids is changed " % self.name)
            self.smtp.Send(
                "server %s[%s] appear process maybe err %s process number changed from %d to %d"
                % (get_host_ip(), get_host_name(), self.name, len(self.ids), len(nids))
            )
            self.ids = nids
            return

        # 再检查下进程个数对不对
        if len(self.ids) < self.number:
            log.Debug(
                "err %s process number[%d] is err but shoud be[%d]"
                % (self.name, len(self.ids), self.number)
            )
            self.smtp.Send(
                "server %s[%s] appear process err %s process number[%d] is err but shoud be[%d]"
                % (
                    get_host_ip(),
                    get_host_name(),
                    self.name,
                    len(self.ids),
                    self.number,
                )
            )
            return

        # 检查进程id有没有变化
        for i in range(len(self.ids)):
            str = ""
            if self.ids[i] != nids[i]:
                self.smtp.Send(
                    "server %s[%s] appear process err %s process maybe restart or to pid changed %d => %d!!!!!!!!!!!!!!!!"
                    % (get_host_ip(), get_host_name(), self.name, self.ids[i], nids[i])
                )
                self.ids = nids
                return

        # 检查进程是否正常
        for pid in self.ids:
            pinfo = p.Process(pid)
            st = pinfo.status()
            mem = self._getMem(pid)
            log.Debug("pid=%u status=%s mem=%s" % (pid, st, self._memToStr(mem)))
            if st == p.STATUS_STOPPED or st == p.STATUS_ZOMBIE or st == p.STATUS_DEAD:
                log.Debug("process %s status=" % pinfo.name(), st)
                self.smtp.Send(
                    "server %s[%s] appear process err %s process status maybe crash[%s] !!!!!!!!!!!!!!!!"
                    % (get_host_ip(), get_host_name(), self.name, st)
                )
                return
            if mem >= self.mem_max_limit:
                self.smtp.Send(
                    "server %s[%s] process warning %s[%d] mem is too big[%d m] now !!!!!!!!!!!!!!!!!!!"
                    % (get_host_ip(), get_host_name(), self.name, pid, mem / 1000)
                )
        log.Debug("%s pids is alive !!!!!!!!!" % self.name)

    def Check(self):
        # 先判断下进程id是否存在
        self._pid_file_check()
        self._pids_check()
